﻿#include "bstrview.h"

#if defined (_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include "bstrwrap.h"

#if defined(MEMORY_DEBUG) || defined(BSTRLIB_MEMORY_DEBUG)
#include "memdbg.h"
#endif

#ifndef bstr__alloc
#define bstr__alloc(x) malloc (x)
#endif

#ifndef bstr__free
#define bstr__free(p) free (p)
#endif

#ifndef bstr__realloc
#define bstr__realloc(p,x) realloc ((p), (x))
#endif

#ifndef bstr__memcpy
#define bstr__memcpy(d,s,l) memcpy ((d), (s), (l))
#endif

#ifndef bstr__memmove
#define bstr__memmove(d,s,l) memmove ((d), (s), (l))
#endif

#ifndef bstr__memset
#define bstr__memset(d,c,l) memset ((d), (c), (l))
#endif

#ifndef bstr__memcmp
#define bstr__memcmp(d,c,l) memcmp ((d), (c), (l))
#endif

#ifndef bstr__memchr
#define bstr__memchr(s,c,l) memchr ((s), (c), (l))
#endif


namespace Bstrlib
{
	CBStringView::CBStringView()
	{
		data = reinterpret_cast<unsigned char*>("");
		slen = 0;
	}

	CBStringView::CBStringView(const char* s)
	{
		data = (unsigned char*)s;
		slen = static_cast<int>(strlen(s));
	}

	CBStringView::CBStringView(int len, const char* s)
	{
		data = (unsigned char*)s;
		slen = len;
	}

	CBStringView::CBStringView(const CBStringView& b)
	{
		data = b.data;
		slen = b.slen;
	}

	CBStringView::CBStringView(const tagbstring& x)
	{
		data = x.data;
		slen = x.slen;
	}

	CBStringView::CBStringView(const void* blk, int len)
	{
		data = (unsigned char*)blk;
		slen = len;
	}

	CBStringView::~CBStringView()
	= default;

	const CBStringView& CBStringView::operator=(const char* s)
	{
		data = (unsigned char*)s;
		slen = static_cast<int>(strlen(s));
		return *this;
	}

	const CBStringView& CBStringView::operator=(const CBStringView& b)
	{
		data = b.data;
		slen = b.slen;
		return *this;
	}

	const CBStringView& CBStringView::operator=(const tagbstring& x)
	{
		data = x.data;
		slen = x.slen;
		return *this;
	}

	bool CBStringView::operator==(const CBStringView& b) const
	{
		return biseq(this, &b) > 0;
	}

	bool CBStringView::operator==(const char* s) const
	{
		return biseqcstr(this, s) > 0;
	}

	bool CBStringView::operator==(const unsigned char* s) const
	{
		return biseqcstr(this, reinterpret_cast<const char*>(s)) > 0;
	}

	bool CBStringView::operator!=(const CBStringView& b) const
	{
		return !((*this) == b);
	}

	bool CBStringView::operator!=(const char* s) const
	{
		return !((*this) == s);
	}

	bool CBStringView::operator!=(const unsigned char* s) const
	{
		return !((*this) == s);
	}

	bool CBStringView::operator<(const CBStringView& b) const
	{
		return bstrcmp(this, &b) < 0;
	}

	bool CBStringView::operator<(const char* s) const
	{
		return strcmp(reinterpret_cast<const char*>(this->data), s) < 0;
	}

	bool CBStringView::operator<(const unsigned char* s) const
	{
		return strcmp(reinterpret_cast<const char*>(this->data), reinterpret_cast<const char*>(s)) < 0;
	}

	bool CBStringView::operator<=(const CBStringView& b) const
	{
		return bstrcmp(this, &b) <= 0;
	}

	bool CBStringView::operator<=(const char* s) const
	{
		return strcmp(reinterpret_cast<const char*>(this->data), s) <= 0;
	}

	bool CBStringView::operator<=(const unsigned char* s) const
	{
		return strcmp(reinterpret_cast<const char*>(this->data), reinterpret_cast<const char*>(s)) <= 0;
	}

	bool CBStringView::operator>(const CBStringView& b) const
	{
		return !((*this) <= b);
	}

	bool CBStringView::operator>(const char* s) const
	{
		return !((*this) <= s);
	}

	bool CBStringView::operator>(const unsigned char* s) const
	{
		return !((*this) <= s);
	}

	bool CBStringView::operator>=(const CBStringView& b) const
	{
		return !((*this) < b);
	}

	bool CBStringView::operator>=(const char* s) const
	{
		return !((*this) < s);
	}

	bool CBStringView::operator>=(const unsigned char* s) const
	{
		return !((*this) < s);
	}

	int CBStringView::caselessEqual(const CBStringView& b) const
	{
		return biseqcaseless(this, &b);
	}

	int CBStringView::caselessCmp(const CBStringView& b) const
	{
		return bstricmp(this, &b);
	}

	int CBStringView::boyer_moore_find(const char* b, int pos) const
	{
		const auto size = slen;
		const auto nsize = strlen(b);
		if (nsize + pos > size || nsize + pos < pos)
		{
			return BSTR_ERR;
		}

		if (nsize == 0)
		{
			return pos;
		}
		const auto haystack = reinterpret_cast<char*>(data);
		const auto nsize_1 = nsize - 1;
		const auto last = b[nsize_1];

		size_t skip = 0;

		auto i = haystack + pos;
		const auto end = haystack + size - nsize_1;

		while (i < end)
		{
			while (i[nsize_1] != last)
			{
				if (++i == end)
				{
					return BSTR_ERR;
				}
			}
			for (size_t j = 0;;)
			{
				if (i[j] != b[j])
				{
					if (skip == 0)
					{
						skip = 1;
						while (skip <= nsize_1 && b[nsize_1 - skip] != last)
						{
							++skip;
						}
					}
					i += skip;
					break;
				}
				if (++j == nsize)
				{
					return i - haystack;
				}
			}
		}
		return BSTR_ERR;
	}

	int CBStringView::boyer_moore_caselessfind(const char* b, int pos) const
	{
		const auto size = slen;
		const auto nsize = strlen(b);
		if (nsize + pos > size || nsize + pos < pos)
		{
			return BSTR_ERR;
		}

		if (nsize == 0)
		{
			return pos;
		}
		const auto haystack = data;
		const auto nsize_1 = nsize - 1;
		const auto last = static_cast<unsigned char>(tolower(b[nsize_1]));

		size_t skip = 0;

		auto i = haystack + pos;
		const auto end = haystack + size - nsize_1;

		while (i < end)
		{
			while (static_cast<unsigned char>(tolower(i[nsize_1])) != last)
			{
				if (++i == end)
				{
					return BSTR_ERR;
				}
			}
			for (size_t j = 0;;)
			{
				if (static_cast<unsigned char>(tolower(i[j])) != static_cast<unsigned char>(tolower(b[j])))
				{
					if (skip == 0)
					{
						skip = 1;
						while (skip <= nsize_1 && static_cast<unsigned char>(tolower(b[nsize_1 - skip])) != last)
						{
							++skip;
						}
					}
					i += skip;
					break;
				}
				if (++j == nsize)
				{
					return i - haystack;
				}
			}
		}
		return BSTR_ERR;
	}

	int CBStringView::find(const CBStringView& b, int pos) const
	{
		return binstr(this, pos, &b);
	}

	int CBStringView::find(const char* b, int pos) const
	{
		const size_t find_str_size = strlen(b);

		if (find_str_size > slen || pos > slen - find_str_size)
		{
			return -1;
		}

		if (find_str_size == 0)
		{
			return pos;
		}

		const unsigned char* end = data + (slen - find_str_size) + 1;
		for (unsigned char* match_try = data + pos;; ++match_try)
		{
			match_try = static_cast<unsigned char*>(memchr(match_try, *b, static_cast<size_t>(end - match_try)));
			if (!match_try)
			{
				return -1;
			}

			if (memcmp(match_try, b, find_str_size) == 0)
			{
				return match_try - data;
			}
		}
	}

	int CBStringView::caselessfind(const CBStringView& b, int pos) const
	{
		return binstrcaseless(this, pos, &b);
	}

	int CBStringView::caselessfind(const char* b, int pos) const
	{
		tagbstring t{};

		if (nullptr == b)
		{
			return BSTR_ERR;
		}

		if (static_cast<unsigned int>(pos) > static_cast<unsigned int>(slen)) return BSTR_ERR;
		if ('\0' == b[0]) return pos;
		if (pos == slen) return BSTR_ERR;

		btfromcstr(t, b);
		return binstrcaseless(this, pos, &t);
	}

	int CBStringView::find(char c, int pos) const
	{
		if (pos < 0) return BSTR_ERR;
		for (; pos < slen; pos++)
		{
			if (data[pos] == static_cast<unsigned char>(c)) return pos;
		}
		return BSTR_ERR;
	}

	int CBStringView::reversefind(const CBStringView& b, int pos) const
	{
		return binstrr(this, pos, &b);
	}

	int CBStringView::reversefind(const char* b, int pos) const
	{
		tagbstring t{};
		if (nullptr == b)
		{
			return BSTR_ERR;
		}
		cstr2tbstr(t, b);
		return binstrr(this, pos, &t);
	}

	int CBStringView::caselessreversefind(const CBStringView& b, int pos) const
	{
		return binstrrcaseless(this, pos, &b);
	}

	int CBStringView::caselessreversefind(const char* b, int pos) const
	{
		tagbstring t{};

		if (nullptr == b)
		{
			return BSTR_ERR;
		}

		if (static_cast<unsigned int>(pos) > static_cast<unsigned int>(slen)) return BSTR_ERR;
		if ('\0' == b[0]) return pos;
		if (pos == slen) return BSTR_ERR;

		btfromcstr(t, b);
		return binstrrcaseless(this, pos, &t);
	}

	int CBStringView::reversefind(char c, int pos) const
	{
		if (pos > slen) return BSTR_ERR;
		if (pos == slen) pos--;
		for (; pos >= 0; pos--)
		{
			if (data[pos] == static_cast<unsigned char>(c)) return pos;
		}
		return BSTR_ERR;
	}

	int CBStringView::findchr(const CBStringView& b, int pos) const
	{
		return binchr(this, pos, &b);
	}

	int CBStringView::findchr(const char* s, int pos) const
	{
		tagbstring t{};
		if (nullptr == s)
		{
			return BSTR_ERR;
		}
		cstr2tbstr(t, s);
		return binchr(this, pos, &t);
	}

	int CBStringView::reversefindchr(const CBStringView& b, int pos) const
	{
		return binchrr(this, pos, &b);
	}

	int CBStringView::reversefindchr(const char* s, int pos) const
	{
		tagbstring t{};
		if (nullptr == s)
		{
			return BSTR_ERR;
		}
		cstr2tbstr(t, s);
		return binchrr(this, pos, &t);
	}

	int CBStringView::nfindchr(const CBStringView& b, int pos) const
	{
		return bninchr(this, pos, &b);
	}

	int CBStringView::nfindchr(const char* s, int pos) const
	{
		tagbstring t{};
		if (nullptr == s)
		{
			return BSTR_ERR;
		}
		cstr2tbstr(t, s);
		return bninchr(this, pos, &t);
	}

	int CBStringView::nreversefindchr(const CBStringView& b, int pos) const
	{
		return bninchrr(this, pos, &b);
	}

	int CBStringView::nreversefindchr(const char* s, int pos) const
	{
		tagbstring t{};
		if (nullptr == s)
		{
			return BSTR_ERR;
		}
		cstr2tbstr(t, s);
		return bninchrr(this, pos, &t);
	}

	CBStringView CBStringView::midstr(int left, int len) const
	{
		if (left < 0)
		{
			len += left;
			left = 0;
		}
		if (len > slen - left) len = slen - left;
		if (len <= 0) return CBStringView{};
		return {data + left, len};
	}
}
